from typing import List, Type, Union

from emi.bar.storage.model import BaseDBModel
from emi.core.DBSource import DBSource, PeeweeModelSource
import peewee
from datetime import datetime, date


class KVModel(BaseDBModel):

    name: str = peewee.CharField(primary_key=True, max_length=128)

    v_float: float = peewee.FloatField(default=None,null=True)
    v_int: float = peewee.IntegerField(default=None,null=True)
    v_str:float = peewee.TextField(null=True)
    v_bytes: bytes = peewee.BlobField(null=True)


    @staticmethod
    def from_data(name:str, value:str):

        db_bar = KVModel()
        db_bar.name = name
        if isinstance(value,float):
            db_bar.v_float = value
        elif isinstance(value,int):
            db_bar.v_int = value
        elif isinstance(value,datetime):
            db_bar.v_float = value.timestamp()
        elif isinstance(value,date):
            db_bar.v_int = value.toordinal()
        elif isinstance(value,str):
            db_bar.v_str = value
        elif isinstance(value,bytes):
            db_bar.v_bytes = value
        else:
            raise NotImplementedError()

        return db_bar


class KVStorage:

    def __init__(self,source:DBSource,table_name = None):

        if table_name is None:
            table_name = 'kv_default'
        self.source = source.init_peewee(table_name,KVModel,indexes=None,ignore_inited=True)

    def get_name_list(self) -> List[str]:
        """
        返回所有的symbol列表
        """
        with self.source.use_peewee() as db_model:
            s = (db_model.select(db_model.name).distinct())
            return [v.name for v in s]

    def put(self,name:str,value:Union[int,float,str,bytes,date,datetime]):
        """
        保存Bar数据。
        """
        assert name is not None
        if value is None:
            ### 删除
            self.clear(name=name)
        else:
            ds = [KVModel.from_data(name,value)]
            with self.source.use_peewee() as db_model:
                db_model.save_all(ds)


    def contians(self,name:str)->bool:
        return self._get(name) is not None

    def get_date(self,name:str,default_value:date = None)->date:
        d_order = self.get_int(name,default_value=None)
        if d_order is None:
            return default_value
        return date.fromordinal(d_order)

    def get_datetime(self,name:str,default_value:datetime = None)->datetime:
        d_order = self.get_float(name,default_value=None)
        if d_order is None:
            return default_value
        return datetime.fromtimestamp(d_order)

    def get_int(self,name:str,default_value:int = None):
        kv_model = self._get(name)
        if kv_model is None:
            return default_value
        return kv_model.v_int

    def get_float(self,name:str,default_value:float = None):
        kv_model = self._get(name)
        if kv_model is None:
            return default_value
        return kv_model.v_float


    def get_str(self,name:str,default_value:str = None):
        kv_model = self._get(name)
        if kv_model is None:
            return default_value
        return kv_model.v_str

    def get_bytes(self,name:str,default_value:bytes = None):
        kv_model = self._get(name)
        if kv_model is None:
            return default_value
        return kv_model.v_bytes

    def _get(self,name:str)->KVModel:
        with self.source.use_peewee() as db_model:
            s = (
                db_model.select().where(db_model.name == name)
                    .first()
            )
            return s

    def size(self) -> int:
        with self.source.use_peewee() as db_model:
            return db_model.select().count()


    def clear(self,name:str = None):
        if name is None:
            with self.source.use_peewee() as db_model:
                db_model.drop_table()
                db_model.create_table()
        else:
            with self.source.use_peewee() as db_model:
                db_model.delete().where(
                    (db_model.name == name)
                ).execute()
